#include "gkt_types.h"
#include "gkt_backboard.h"
#include "gkt_debug.h"
#include "gkt_system.h"
#include "gkt_os.h"
#include <string.h>
enum {
	COMMON_OP_GET_INFO = 0,
	COMMON_OP_INFO,
	COMMON_OP_STANDBY,
	COMMON_OP_FACTORY_RESET,
	COMMON_OP_SET_INFO,
	COMMON_OP_CHECK_PAIRING,

	COMMON_OP_MAX
};

enum {
	COMMON_RSP_STATUS = GKT_BB_APP_PACKET_MINSIZE,
	COMMON_RSP_MAX
};

/* 0 ~ 15bits: info attr bits */
#define BB_COMMON_F_TRANSPARENT	(1U << 0)
#define BB_COMMON_F_READY	(1U << 31)

#define BB_COMMON_CHECK_PAIRING_RETRY_COUNT_MAX 3

typedef struct {
	uint32_t	flags;
	union {
		struct {
			uint16_t	hw_version;
			uint16_t	sw_version;
		};
		uint32_t	version;
	};
	uint32_t	module_bits;

	gkt_soft_timer_t timer_id;
	int retry_count;

	gkt_backboard_common_notify_fn notify;
} bb_common_s;
static bb_common_s s_bb_common;

int bb_common_is_ready(void)
{
	return (s_bb_common.flags & BB_COMMON_F_READY) ? 1 : 0;
}

enum {
	COMMON_INFO_ATTR_BITS = GKT_BB_APP_PACKET_MINSIZE,
	COMMON_INFO_HW_VERSION,
	COMMON_INFO_SW_VERSION = COMMON_INFO_HW_VERSION + 2,
	COMMON_INFO_MODULE_BITS = COMMON_INFO_SW_VERSION + 2,
	COMMON_INFO_MAX = COMMON_INFO_MODULE_BITS + 2
};
static void bb_common_info_notify(int type, 
				const uint8_t *data, int length_retval)
{
	if ((GKT_BB_NOTIFY_PEER == type)
		&& (length_retval >= COMMON_INFO_MAX))
	{
		s_bb_common.flags = BB_COMMON_F_READY | data[COMMON_INFO_ATTR_BITS];
		s_bb_common.hw_version = gkt_fetch_be16(&data[COMMON_INFO_HW_VERSION]);
		s_bb_common.sw_version = gkt_fetch_be16(&data[COMMON_INFO_SW_VERSION]);
		s_bb_common.module_bits = gkt_fetch_be16(&data[COMMON_INFO_MODULE_BITS]);
		gkt_info("bb_common: flags(0x%x), version(0x%x - 0x%x), mod_bits(0x%x)\n",
			s_bb_common.flags, s_bb_common.hw_version, 
			s_bb_common.sw_version, s_bb_common.module_bits);
	}
}
int gkt_backboard_common_get_info(void)
{
	uint8_t data[GKT_BB_APP_PACKET_MINSIZE];

	data[GKT_BB_APP_PACKET_MODULE_ID] = GKT_BB_MODULE_COMMON;
	data[GKT_BB_APP_PACKET_OPCODE] = COMMON_OP_GET_INFO;
	return gkt_backboard_send_data(data, GKT_BB_APP_PACKET_MINSIZE);
}

static void backbaord_common_inner_notify(uint32_t type, uint32_t status)
{
	gkt_trace("backbaord_common_inner_notify: type(%u), status(%u)\n", type, status);
	if (s_bb_common.notify)
		(*s_bb_common.notify)(type, status);
}
void gkt_backboard_common_register_notify(gkt_backboard_common_notify_fn notify)
{
	s_bb_common.notify = notify;
}
uint16_t gkt_backboard_get_hw_version(void)
{
	return s_bb_common.hw_version;
}
uint16_t gkt_backboard_get_sw_version(void)
{
	return s_bb_common.sw_version;
}

int gkt_backboard_notify_frontboard_standby_status(void)
{
	uint8_t data[GKT_BB_APP_PACKET_MINSIZE];

	data[GKT_BB_APP_PACKET_MODULE_ID] = GKT_BB_MODULE_COMMON;
	data[GKT_BB_APP_PACKET_OPCODE] = COMMON_OP_STANDBY;
	return gkt_backboard_send_data(data, GKT_BB_APP_PACKET_MINSIZE);
}

static void bb_common_factory_reset_notify(int type, 
				const uint8_t *data, int length_retval)
{
	uint8_t bb_status;
	uint8_t notify_status;

	if (GKT_BB_NOTIFY_PEER == type) {
		if (length_retval < COMMON_RSP_STATUS) {
			gkt_error("bb_common_factory_reset_notify: <PEER> data length invalid!\n");
			return;
		}

		bb_status = data[COMMON_RSP_STATUS];
		gkt_trace("bb_common_factory_reset_notify: <PEER> bb_status(%u)\n",
			bb_status);

		if ((GKT_BB_STATUS_START == bb_status)
			|| (GKT_BB_STATUS_INPROCESS == bb_status))
		{
			notify_status = GKT_BACKBAORD_COMMON_STATUS_FACTORY_RESET_INPROCESS;
		}
		else if ((GKT_BB_STATUS_SUCCESS == bb_status)
			|| (GKT_BB_STATUS_COMPLETE == bb_status)) 
		{
			notify_status = GKT_BACKBAORD_COMMON_STATUS_FACTORY_RESET_COMPLETE;
		}
		else if (bb_status >= GKT_BB_STATUS_ERR_BASE) {
			notify_status = GKT_BACKBAORD_COMMON_STATUS_FACTORY_RESET_FAILED;
		}
		else
			return;
	}
	else if (length_retval < GKT_SUCCESS) {
		gkt_error("bb_common_factory_reset_notify: <LOCAL> retval(%d)\n",
			length_retval);
		notify_status = GKT_BACKBAORD_COMMON_STATUS_FACTORY_RESET_FAILED;
	}
	else
		return;

	backbaord_common_inner_notify(GKT_BACKBAORD_COMMON_NOTIFY_TYPE_STATUS, notify_status);
}
enum {
	COMMON_FACTORY_RESET_MODULE_BITS_HIGH = GKT_BB_APP_PACKET_MINSIZE,
	COMMON_FACTORY_RESET_MODULE_BITS_LOW,
	COMMON_FACTORY_RESET_MAX
};
int gkt_backboard_factory_reset(uint32_t module_bits)
{
	uint8_t data[COMMON_FACTORY_RESET_MAX];

	data[GKT_BB_APP_PACKET_MODULE_ID] = GKT_BB_MODULE_COMMON;
	data[GKT_BB_APP_PACKET_OPCODE] = COMMON_OP_FACTORY_RESET;
	gkt_store_be16(&data[COMMON_FACTORY_RESET_MODULE_BITS_HIGH], module_bits);
	return gkt_backboard_send_data(data, COMMON_FACTORY_RESET_MAX);
}

#if defined(GKT_CONFIG_BACKBOARD_CHECK_PAIRING) && GKT_CONFIG_BACKBOARD_CHECK_PAIRING
static void bb_common_timer_callback(void *p)
{
	int retval;

	retval = gkt_backboard_check_pairing();
	if (retval != GKT_SUCCESS) {
		if (s_bb_common.retry_count < BB_COMMON_CHECK_PAIRING_RETRY_COUNT_MAX) {
			retval = gkt_soft_timer_start(s_bb_common.timer_id, 1000);
			if (GKT_SUCCESS == retval)
				s_bb_common.retry_count++;
			else
				gkt_soft_timer_stop(s_bb_common.timer_id);
		}
	}
	else
		s_bb_common.retry_count = 0;
}
static void bb_common_check_pairing_notify(int type, const uint8_t *data, int length_retval)
{
	uint8_t bb_status;
	uint8_t notify_status;

	if (GKT_BB_NOTIFY_PEER == type) {
		if (length_retval < COMMON_RSP_STATUS) {
			gkt_error("bb_common_check_pairing_notify: <PEER> data length invalid!\n");
			return;
		}

		bb_status = data[COMMON_RSP_STATUS];
		gkt_trace("bb_common_check_pairing_notify: <PEER> bb_status(%u)\n",
			bb_status);

		if (GKT_BB_STATUS_SUCCESS == bb_status)
			notify_status = GKT_BACKBAORD_COMMON_STATUS_CHECK_PAIRING_SUCCESS;
		else if (GKT_BB_STATUS_ERR_DATA_VALUE == bb_status) {
			if (GKT_SUCCESS != gkt_soft_timer_start(s_bb_common.timer_id, 10)) {
				gkt_error("bb_common: timer start failed\n");
				gkt_soft_timer_stop(s_bb_common.timer_id);
			}
			return;
		}
		else if (GKT_BB_STATUS_ERR_FORBID == bb_status)
			notify_status = GKT_BACKBAORD_COMMON_STATUS_CHECK_PAIRING_FAILED;
		else
			return;

		backbaord_common_inner_notify(GKT_BACKBAORD_COMMON_NOTIFY_TYPE_STATUS, notify_status);
	}
	else if (length_retval < GKT_SUCCESS){
		gkt_error("bb_common_check_pairing_notify: <LOCAL> retval(%d)\n", length_retval);
	}

}
enum {
	COMMON_CHECK_PAIRING_CHIPID = GKT_BB_APP_PACKET_MINSIZE,
	COMMON_CHECK_PAIRING_MAX = COMMON_CHECK_PAIRING_CHIPID+GKT_SYSTEM_CHIPID_BYTES
};
int gkt_backboard_check_pairing(void)
{
	static uint8_t data[COMMON_CHECK_PAIRING_MAX];//Larger than 6 bytes cannot use stack area
	int i;
	if (NULL == g_system_info.chip_id)
		return GKT_EPARAM;

	data[GKT_BB_APP_PACKET_MODULE_ID] = GKT_BB_MODULE_COMMON;
	data[GKT_BB_APP_PACKET_OPCODE] = COMMON_OP_CHECK_PAIRING;
	for (i=0; i<GKT_SYSTEM_CHIPID_BYTES; i++)
		data[COMMON_CHECK_PAIRING_CHIPID+i] = g_system_info.chip_id[i];

	return gkt_backboard_send_data(data, COMMON_CHECK_PAIRING_MAX);
}
#endif
static void bb_common_notify(int type, 
				const uint8_t *data, int length_retval)
{
	uint8_t opcode;
	gkt_info("bb_common_notify: type(%d), length_retval(%d)\n", type, length_retval);
	opcode = data[GKT_BB_APP_PACKET_OPCODE];
	if (COMMON_OP_INFO == opcode)
		bb_common_info_notify(type, data, length_retval);
	else if (COMMON_OP_FACTORY_RESET == opcode) {
		bb_common_factory_reset_notify(type, data, length_retval);
	}
#if defined(GKT_CONFIG_BACKBOARD_CHECK_PAIRING) && GKT_CONFIG_BACKBOARD_CHECK_PAIRING
	else if (COMMON_OP_CHECK_PAIRING == opcode) {
		bb_common_check_pairing_notify(type, data, length_retval);
	}
#endif
}

int gkt_backboard_common_init(void)
{
	s_bb_common.flags = 0;
	s_bb_common.version = 0;
	s_bb_common.module_bits = 0;
	s_bb_common.notify = NULL;
#if defined(GKT_CONFIG_BACKBOARD_CHECK_PAIRING) && GKT_CONFIG_BACKBOARD_CHECK_PAIRING
	s_bb_common.retry_count = 0;
	s_bb_common.timer_id = gkt_soft_timer_create(GKT_SOFT_TIMER_ONCE, bb_common_timer_callback, NULL);
	if (!s_bb_common.timer_id) {
		gkt_error("common_bb: create soft timer failed!\n");
		return GKT_ECREATE;
	}
#endif
	gkt_backboard_register_module(GKT_BB_MODULE_COMMON,
		COMMON_OP_MAX, bb_common_notify);

	gkt_backboard_common_get_info();

	return GKT_SUCCESS;
}


